package com.wmz.config;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wmz.constant.AuthConstants;
import com.wmz.constant.BusinessEnum;
import com.wmz.constant.HttpConstants;
import com.wmz.impl.UserDetailServiceImpl;
import com.wmz.model.LoginResult;
import com.wmz.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import java.io.PrintWriter;
import java.time.Duration;
import java.util.UUID;

/**
 * @author wmz
 * @date 2024/7/2 20:56
 * @description SpringSecurity安全框架配置类
 */
@Configuration
@Slf4j
public class AuthSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailServiceImpl userDetailService;

    @Autowired
    private StringRedisTemplate redisTemplate;
    /**
     * 设置Security安全框架走我们自己设置的认证流程
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailService);
    }

    /**
     * 网络请求配置
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //关闭跨站请求伪造
        http.cors().disable();
        //关闭跨域请求
        http.csrf().disable();
        //关闭session使用策略
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //配置登录信息
        http.formLogin()
                .loginProcessingUrl(AuthConstants.LOGIN_URL) //设置登录URL
                .successHandler(authenticationSuccessHandler())   //设置登录成功后的处理类
                .failureHandler(authenticationFailureHandler());   //设置登录失败后的处理类
        //配置登出信息
        http.logout()
                .logoutUrl(AuthConstants.LOGOUT_URL) //设置登出URL
                .logoutSuccessHandler(logoutSuccessHandler()); //设置登出成功后的处理类

        //要求所有请求必须认证通过
        http.authorizeRequests().anyRequest().authenticated();
    }

    /**
     * 登录成功处理器
     * @return
     */
    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler() {
        return (request, response, authentication) -> {
            //设置响应信息
            response.setContentType(HttpConstants.APPLICATION_JSON);
            response.setCharacterEncoding(HttpConstants.UTF_8);

            //使用UUID当作Token
            String token = UUID.randomUUID().toString();
            String userJsonStr = JSON.toJSONString(authentication.getPrincipal());
            //将token当作key，用户信息当作value放入redis中，默认保存4个小时
            redisTemplate.opsForValue().set(AuthConstants.LOGIN_TOKEN_PREFIX + token, userJsonStr, Duration.ofSeconds(AuthConstants.TOKEN_TIME));

            //封装一个登录统一结果对象
            LoginResult loginResult = new LoginResult(token, AuthConstants.TOKEN_TIME);
            //创建一个响应结果对象
            Result<LoginResult> result = Result.success(loginResult);

            //返回结果
            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString(result);
            PrintWriter writer = response.getWriter();
            writer.write(s);
            writer.flush();
            writer.close();
        };
    }

    /**
     * 登录失败处理器
     * @return
     */
    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler() {
        return (request, response, e) -> {
            //设置响应头信息
            response.setContentType(HttpConstants.APPLICATION_JSON);
            response.setCharacterEncoding(HttpConstants.UTF_8);

            //创建统一响应结果对象
            Result<Object> result = new Result<>();
            result.setCode(BusinessEnum.UN_AUTHORIZAION.getCode());
            if(e instanceof BadCredentialsException) {
                result.setMsg("用户名或密码有误");
            }else if(e instanceof UsernameNotFoundException){
                result.setMsg("用户不存在");
            }else if(e instanceof AccountExpiredException){
                result.setMsg("账号异常，请联系管理员");
            }else if(e instanceof InternalAuthenticationServiceException){
                result.setMsg(e.getMessage());
            }else {
                result.setMsg(BusinessEnum.OPERATION_FAIL.getDesc());
            }
            //返回结果
            String s = new ObjectMapper().writeValueAsString(result);
            PrintWriter writer = response.getWriter();
            writer.write(s);
            writer.flush();
            writer.close();
        };
    }

    /**
     * 登出成功处理器
     * @return
     */
    @Bean
    public LogoutSuccessHandler logoutSuccessHandler() {
        return (request, response, authentication) -> {
            //设置响应头信息
            response.setContentType(HttpConstants.APPLICATION_JSON);
            response.setCharacterEncoding(HttpConstants.UTF_8);
            //从请求头中获取token
            String authorization = request.getHeader(AuthConstants.AUTHORIZATION);
            String token = authorization.replaceFirst(AuthConstants.BEARER, "");
            //从redis中删除token
            redisTemplate.delete(AuthConstants.LOGIN_TOKEN_PREFIX + token);
            //创建统一响应结果对象
            Result result = Result.success(null);
            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString(result);
            //返回结果
            PrintWriter writer = response.getWriter();
            writer.write(s);
            writer.flush();
            writer.close();
        };
    }

    /**
     * 配置密码加密器
     * @return
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
